[Xamarin.Mac] データバインディングでコントロールと連携してみました
1 はじめに
CX事業本部の平内(SIN)です。
Xamarin.Macを使用すると、C#でネイティブなMacのアプリが作成可能です。 ここでは、私自身がXamarin.Macに入門して学習した事項を覚書として書かせて頂いています。
今回は、データバインディングを使用して、コントロールと連携してみました。
Xamarin.Macでは、規定の属性を設定することで、C#からKey-Value Observing Programmingにアクセスが可能です。
2 商品クラス
簡単なクラスを元に確認を進めます。
下記は、「名前」と「価格」だけを保持する「商品」クラスです。名前(_name)と価格(_price)をプライベートで保持し、アクセス要領をプロパティ(Name及び、Age)で公開しています。
public class Product { private string _name; private int _price; public Product(string name, int price) { _name = name; _price = price; } public string Name { get { return _name; } set { _name = value; } } public int Price { get { return _price; } set { _price = value; } } }
3 Objective-Cにクラスを公開
続いて、商品クラスをデータバインディング可能なクラスに変更します。 作業は、以下の3つです。
- NSObject(または、NSObjectを継承クラス)から継承
- Registerでクラスを登録
- Exportでプロパティを公開
- WillChangeValue及び、DidChangeValueで通知先プロパティを指定
なお、RegisterとExportで指定する名前は、あくまでObjective-Cに向けて公開される名前であって、元のクラス名やプロパティ名と一致している必要はありません。
[Register("Product")] public class Product : NSObject { private string _name; private int _price; public Product(string name, int price) { _name = name; _price = price; } [Export("Name")] public string Name { get { return _name; } set { WillChangeValue("Name"); _name = value; DidChangeValue("Name"); } } [Export("Price")] public int Price { get { return _price; } set { WillChangeValue("Price"); _price = value; DidChangeValue("Price"); } } }
4 操作
(1) ValueForKey / SetValueForKey
データバインディング可能になったクラスは、ValueForKey及び、SetValueForKeyでプロパティ値の取得・設定が可能になります。
// 商品クラスを生成 var product = new Product("AAA",200); // 名前を変更 product.SetValueForKey(new NSString("BBB"), new NSString("Name")); // 名前及び、価格を取得 var name = product.ValueForKey(new NSString("Name")); var price = product.ValueForKey(new NSString("Price")); // 確認 Console.WriteLine($"name={name} price={price}");
出力
name=BBB price=200
(2) AddObserver
AddObserverで、指定したプロパティの変化を受け取ることが出来ます。
// 商品クラスを生成 var product = new Product("AAA",200); // オブザーバを設定(Nameプロパティの値が変化した時にイベントを登録) product.AddObserver("Name", NSKeyValueObservingOptions.New, (sender) => { // 変化した値を確認 Console.WriteLine($"New Name: {product.Name}"); }); // 名前の変更 product.SetValueForKey(new NSString("BBB"), new NSString("Name")); // 名前及び、価格を取得 var name = product.ValueForKey(new NSString("Name")); var price = product.ValueForKey(new NSString("Price")); // 確認 Console.WriteLine($"name={name} price={price}");
出力
New Name: BBB New Name: BBB name=BBB price=200
5 コントロールとの同期
簡単なUIを作成して、商品クラスとデータ連携してみました。
(1) データの公開
ViewControllerで保持されるデータ(product)を、プロパティとして定義し、[Export("Product")] で公開します。
public partial class ViewController : NSViewController { // データの定義(生成) Product product = new Product("AAA", 100); // プロパティとして公開 [Export("Product")] public Product Product { get { return product; } set { WillChangeValue("Product"); product = value; DidChangeValue("Product"); } } public override void ViewDidLoad() { base.ViewDidLoad(); // 変化を確認するために、オブザーバーを定義する product.AddObserver("Name", NSKeyValueObservingOptions.New, (sender) => { Console.WriteLine($"New Name: {product.Name}"); }); }
(2) Interface Builder から bind
ウインドウにText Fieldを追加し、バインド設定します。
Bind toにチェックを入れて、バインド先をViewControllerとし、その中の、ProductのNameに紐づけています。
self.Product.Name
ここで、selfは、当該コントロールが配置されているビューコントローラーで、ここでは、ViewControllerとなります。
実行してみると、初期化されたProduct.Nameの値である「AAA」が、TextFieltの値として表示され、変更すると、Observerでトリガしたイベントにより、その内容がコンソールに出力されている事が確認できます。
6 最後に
今回は、簡単なデータバインディングの動作を確認してみました。
UIコントロールへの表示や、UIで変更された値の反映は、様々な実装が必要になりますが、バインディングを使用することで、その実装量は結構省力化されるかも知れません。 ビューとロジックとの分離のためにも、積極的に利用していきたいところです。